package com.github.jexl;

import com.google.common.collect.Maps;
import org.apache.commons.jexl3.*;
import org.apache.commons.lang3.math.NumberUtils;
import org.junit.jupiter.api.Test;

import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * @author zhangcheng
 * @date 2022/11/3 9:05
 * @description JEXL的全称是Java表达式语言(Java Expression Language)，
 * 简单的说，它可以配合我们的Java程序运算一些简单的表达式，由于近期的项目的中用到了，所以记录一下它的使用方法，很简单。
 * 具体可以识别哪些表达式？其中包含最基本的加减乘除、大于小于等于、与或非、按位与、按位或、按位异或、按位补码，
 * 除此之外还可以调用Java程序中方法，比如要判断一个字符串是否包含某一个字符，
 * 就可以调用String的contains()方法来判断，在下文中会出现这样的事例。
 * 当然远不止这些基本的运算符，
 * 想要了解更多的话可以去看官方文档：https://commons.apache.org/proper/commons-jexl/reference/syntax.html
 */
public class JexlTest {

    private static final JexlEngine jexl = new JexlBuilder().cache(512).strict(true).silent(false).create();

    public static void main(String[] args) {
        // 创建表达式引擎对象
// 创建表达式语句
        String expressionStr = "money > 5000";
// 创建Context对象，为表达式中的未知数赋值
        JexlContext context = new MapContext();
        context.set("money", "10000");
// 使用表达式引擎创建表达式对象
        JexlExpression expression = jexl.createExpression(expressionStr);
// 使用表达式对象计算
        Object evaluate = expression.evaluate(context);
// 输出结果：true
        System.out.println(evaluate);

    }

    @Test
    public void test() {
        // 创建表达式语句
        String expressionStr = "(2 * (1 + 5000)+1)";
        // 创建Context对象，为表达式中的未知数赋值
        JexlContext context = new MapContext();
        context.set("money", "10000");
        // 使用表达式引擎创建表达式对象
        JexlExpression expression = jexl.createExpression(expressionStr);
        // 使用表达式对象计算
        Object evaluate = expression.evaluate(context);
        // 输出结果：true
        System.out.println(evaluate);
    }

    @Test
    public void test1() {
        // 创建表达式引擎对象
        // 创建表达式语句
        String expressionStr = "($1000 * (1 + 5000)+1)";
        // 创建Context对象，为表达式中的未知数赋值
        JexlContext context = new MapContext();
        context.set("$1000", 2F);
        // 使用表达式引擎创建表达式对象
        JexlExpression expression = jexl.createExpression(expressionStr);
        // 使用表达式对象计算
        Float evaluate = (Float) expression.evaluate(context);
        // 输出结果：true
        System.out.println(evaluate);
    }

    @Test
    public void test_formula_1() {
        // 1、创建 JexlBuilder
        JexlBuilder jexlBuilder = new JexlBuilder().cache(512).strict(true).silent(false);

        // 2、用户自定义函数（调用方式--》函数名:方法名）
        Map<String, Object> funcs = Maps.newLinkedHashMap();
        funcs.put("fn_min", new MinFunction());
        jexlBuilder.namespaces(funcs);

        // 3、创建 JexlEngine
        JexlEngine jexl = jexlBuilder.create();

        // 4、创建表达式对象(函数:方法名)
        String jexlExp = "fn_min:exec(x,y,x)";  // 表达式
        JexlExpression e = jexl.createExpression(jexlExp);

        // 5、创建 context，用于传递参数
        JexlContext jc = new MapContext();
        jc.set("x", 8);
        jc.set("y", 1);
        jc.set("z", 3);

        // 6、执行表达式
        Object o = e.evaluate(jc);
        System.out.println("返回结果：" + o);
    }

    // 取最小值函数
    public class MinFunction {
        public Object exec(Object... args) {
            return Stream.of(args)
                    .mapToDouble(i -> {
                        String iVal = Objects.toString(i, null);
                        return NumberUtils.isCreatable(iVal) ? NumberUtils.toDouble(iVal) : 0;
                    }).min()
                    .orElse(0);
        }
    }

    @Test
    public void test3() {
        // 创建表达式引擎对象
        // 创建表达式语句
        String expressionStr = "$1000 > 1";
        // 创建Context对象，为表达式中的未知数赋值
        JexlContext context = new MapContext();
        context.set("$1000", 2F);
        // 使用表达式引擎创建表达式对象
        JexlExpression expression = jexl.createExpression(expressionStr);
        // 使用表达式对象计算
        Boolean evaluate = (Boolean) expression.evaluate(context);
        // 输出结果：true
        System.out.println(evaluate);
    }

    @Test
    public void test4() {
        // 创建表达式引擎对象
        // 创建表达式语句
//        String expressionStr = "($1000 * (1 + 5000)+1)";
        String expressionStr = "($1000 * (1 + 5000)+0)";
        // 创建Context对象，为表达式中的未知数赋值
        JexlContext context = new MapContext();
        context.set("$1000", 0);
        // 使用表达式引擎创建表达式对象
        JexlExpression expression = jexl.createExpression(expressionStr);
        // 使用表达式对象计算
        Number evaluate = (Number) expression.evaluate(context);
        // 输出结果：true
        Double d = evaluate.doubleValue();
        Float f = evaluate.floatValue();
        System.out.println(evaluate);
    }


    @Test
    public void test5() {
        Float f = 99999999999999999999999999999999999999.99999999999999999999999999999999F;
        Double d = ((Number) f).doubleValue();
        f = ((Number) d).floatValue();

        d = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.99999999999999999999999999D;

        f = ((Number) d).floatValue();
        d = ((Number) f).doubleValue();
    }

}
